/**
 * 
 */
package com.stulsoft.yssmugmugutils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.kallasoft.smugmug.api.json.entity.Album;
import com.kallasoft.smugmug.api.json.entity.Category;
import com.kallasoft.smugmug.api.json.v1_2_1.login.WithPassword;
import com.kallasoft.smugmug.api.json.v1_2_1.logout.Logout;

/**
 * This class allows to build a album tree.
 * 
 * @author Yuriy Stul
 * 
 */
public class TreeBuilder {
	private final String username;
	private final String password;
	private final Map<String, YSCategory> categories = new TreeMap<String, YSCategory>();
	private final Logger logger = LogManager.getLogger(TreeBuilder.class);
	private final static String XML_FILE_NAME = "categoryTree.xml";

	/**
	 * Initializes a new instance of the TreeBuilder class.
	 * 
	 * @param username
	 *            - the Smugmug's user name.
	 * @param password
	 *            - the Smugmug's password.
	 */
	public TreeBuilder(String username, String password) {
		if (username == null || username.length() == 0) {
			throw new NullPointerException("username is null or empty.");
		}
		if (password == null || password.length() == 0) {
			throw new NullPointerException("password is null or empty.");
		}

		this.username = username;
		this.password = password;
	}

	/**
	 * Builds the three of the categories, subcategories, and albums.
	 */
	public void buildTree() {
		int albumCounter = 0;
		int categoryCounter = 0;
		int subcategoryCounter = 0;

		try {
			Program.markProcess();
			WithPassword withPassword = new WithPassword();
			WithPassword.WithPasswordResponse response = withPassword.execute(
					Constants.API_URL, Constants.API_KEY, username, password);
			String sessionID = response.getSessionID();

			com.kallasoft.smugmug.api.json.v1_2_1.albums.Get get = new com.kallasoft.smugmug.api.json.v1_2_1.albums.Get();
			com.kallasoft.smugmug.api.json.v1_2_1.albums.Get.GetResponse albums = get
					.execute(Constants.API_URL, Constants.API_KEY, sessionID,
							false);

			Program.markProcess();

			for (Album album : albums.getAlbumList()) {
				Program.markProcess();

				++albumCounter;
				Category category = album.getCategory();

				YSCategory ysCategory = categories.get(category.getName());
				if (ysCategory == null) {
					ysCategory = new YSCategory(category.getID(),
							category.getName());
					categories.put(ysCategory.getName(), ysCategory);
					++categoryCounter;
				}

				Category subCategory = album.getSubCategory();

				if (subCategory != null) {
					YSCategory ysSubcategory = ysCategory
							.getSubcategory(subCategory.getID());
					if (ysSubcategory == null) {
						ysSubcategory = new YSCategory(subCategory.getID(),
								subCategory.getName());
						ysCategory.addSubcategory(ysSubcategory);
						++subcategoryCounter;
					}

					ysSubcategory.addAlbum(album.getTitle());
				} else {
					ysCategory.addAlbum(album.getTitle());
				}
			}

			Logout logout = new Logout();
			logout.execute(Constants.API_URL, Constants.API_KEY, sessionID);

			saveTreeAsXml("categoryTree.xml");
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(
					"Can't get information from Smugmug site. Error: "
							+ e.getMessage());
		}

		System.out.println();

		String newLine = System.getProperty("line.separator");
		StringBuilder buffer = new StringBuilder("Totals:");
		buffer.append(newLine);
		buffer.append("Total albums: " + albumCounter);
		buffer.append(newLine);
		buffer.append("Total categories: " + categoryCounter);
		buffer.append(newLine);
		buffer.append("Total subcategories: " + subcategoryCounter);

		System.out.println(buffer.toString());
		logger.info(buffer.toString());
	}

	/**
	 * Saves the tree as XML file.
	 * 
	 * @param path
	 *            - specifies a location of the XML file.
	 */
	private void saveTreeAsXml(String path) {
		try {
			Document document = XmlUtils.createDocument();
			Element rootElement = document.createElement("tree");
			document.appendChild(rootElement);

			for (Map.Entry<String, YSCategory> entry : categories.entrySet()) {
				YSCategory ysCategory = entry.getValue();
				
				Program.markProcess();

				Element categoryElement = document.createElement("category");
				categoryElement.setAttribute("name", ysCategory.getName());
				rootElement.appendChild(categoryElement);

				for (YSCategory ysSubcategory : ysCategory.getSubcategories()) {
					Program.markProcess();

					Element subcategoryElement = document
							.createElement("subcategory");
					subcategoryElement.setAttribute("name",
							ysSubcategory.getName());
					categoryElement.appendChild(subcategoryElement);

					for (String album : ysSubcategory.getAlbums()) {
						Element albumElement = document.createElement("album");
						albumElement.setAttribute("name", album);
						subcategoryElement.appendChild(albumElement);
					}
				}

				for (String album : ysCategory.getAlbums()) {
					Element albumElement = document.createElement("album");
					albumElement.setAttribute("name", album);
					categoryElement.appendChild(albumElement);
				}
			}

			XmlUtils.saveToFile(document, XML_FILE_NAME);

		} catch (ParserConfigurationException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException("Can't get XML parser. Error: "
					+ e.getMessage());
		} catch (TransformerConfigurationException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException("Can't get XML transformer. Error: "
					+ e.getMessage());
		} catch (FileNotFoundException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException("Can't open file " + XML_FILE_NAME
					+ ". Error: " + e.getMessage());
		} catch (TransformerException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException("Error during saving XML file. Error: "
					+ e.getMessage());
		} catch (IOException e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException("I/O error: " + e.getMessage());
		}
	}
}
